package chapter_13;

// 字符串处理

/*
13.1 String构造函数
String s = new String();
将创建一个String实例，该实例中不包含字符。

String(char chars[ ])
下面是一个例子：
char chars[] = { 'a', 'b', 'c' };
String s = new String(chars);

使用下面的构造函数可以指定字符数组的一个子区域作为初始化值。
String(char chars[ ], int startIndex, int numChars)
这里，startIndex指定了子区域开始的下标，numChars指定所用字符的个数。下面是一
个例子：
char chars[] = { 'a', 'b', 'c', 'd', 'e', 'f' };
String s = new String(chars, 2, 3);
该例子用字符cde初始化s。

用下面的构造函数可以构造一个String对象，该对象包括了与另一个String对象相同的
字符序列。
String(String strObj)

尽管Java的Char类型使用16位（bit）表示Unicode编码字符集，在Internet中，字符串的
典型格式使用由ASCII字符集构成的8位字节数组。因为8位ASCII字符串是共同的，当给定
一个字节（byte）数组时，String类提供了初始化字符串的构造函数。它们的形式如下：
String(byte asciiChars[ ])
String(byte asciiChars[ ], int startIndex, int numChars)
第13 章字符串处理 241
这里asciiChars指定了字节数组。第二种形式允许指定一个子区域。在这些构造函数中，
通过使用操作平台默认的字符编码实现了由字节到字符的转换，下面的程序举例说明了这
些构造函数：
// Construct string from subset of char array.
class SubStringCons {
    public static void main(String args[]) {
        byte ascii[] = {65, 66, 67, 68, 69, 70 };
        String s1 = new String(ascii);
        System.out.println(s1);
        String s2 = new String(ascii, 2, 3);
        System.out.println(s2);
    }
}
该程序运行产生如下的输出：
ABCDEF
CDE

注意：当从一个数组创建一个String对象时，数组的内容将被复制。在字符串被
创建以后，如果改变数组的内容，String将不会随之改变。
 */


/*
13.2 字符串长度
字符串的长度是指其所包含的字符的个数。调用如下的length( )方法可以得到这个值：
int length( )
下面的程序段输出“3”，因为在字符串s中有三个字符。
char chars[] = { 'a', 'b', 'c' };
String s = new String(chars);
System.out.println(s.length());
 */


/*
13.3 特殊的字符串操作
    13.3.1 字符串文字

    13.3.2 字符串连接
    通常，Java不允许对String对象进行操作。这一规则的一个例外是+运算符，它可以连
    接两个字符串，产生一个String对象。

    13.3.3 字符串与其他类型数据的连接
    字符串可以和其他类型的数据连接。例如，考虑与前面的例子略有差别的一个例子：
    int age = 9;
    String s = "He is " + age + " years old.";
    System.out.println(s);

    应当小心的是当你将其他类型的操作与字符串连接表达式混合时，有可能得到意想不
    到的结果。看下面例子：
    String s = "four: " + 2 + 2;
    System.out.println(s);
    程序显示：
    four: 22
    虽然这可能是你期望得到的。这是为什么呢？这是因为运算符的优先级造成了首先是
    “four”和与2相应的字符串的连接，这个连接的结果再和与2相应的字符串连接。若要先
    实现整数加，就应该使用圆括号。像下面这样：
    String s = "four: " + (2 + 2);
    现在s包含了字符串“four：4”。

    13.3.4 字符串转换和toString()
    每一个类都执行toString( )方法，因为它是由Object定义的。然而toString( )方法的默认
    实现是不够的。对于所创建的大多数类，通常想用你自己提供的字符串表达式重载toString( )
    方法。幸运的是这很容易做到。toString( )方法具有如下的一般形式：
    String toString( )
    实现toString( )方法，仅仅返回一个String对象，该对象包含描述类中对象的可读的字符
    串。
    通过对所创建类的toString( )方法的覆盖，允许将得到的字符串完全集成到Java的程序
    设计环境中。例如它们可以被用于print( )和println( )语句以及连接表达式中。
 */


/*
13.4 字符截取
    13.4.1 charAt()
    为了从一个字符串（String）中截取一个字符，可以通过charAt( )方法直接引用单个字
    符。其一般形式如下：
    char charAt(int where)
    这里，where是想要得到的字符的下标。where的值必须是非负的，它指定了在字符串
    中的位置。charAt( )方法返回指定位置的字符。

    13.4.2 getChars()
    如果想一次截取多个字符，可以使用getChars( )方法。它有如下的一般形式：
    void getChars(int sourceStart, int sourceEnd, char target[ ], int
    targetStart)
    这里sourceStart指定了子字符串开始的下标，sourceEnd指定了子字符串结束的下一个
    字符的下标。因此子字符串包含了从sourceStart到sourceEnd–1的字符。获得字符的数组由
    target所指定。将被复制子字符串于其中的target的下标由targetStart指定。注意必须确保的
    是数组target应该足够大以保证能容纳被指定子字符串中的字符。

    13.4.3 getBytes()
    有一种称为getBytes( )的方法，它是实现将字符存放于字节数组中的getChars( )方法的
    替代，它使用平台提供的默认的字符到字节的转换。下面是它的最简单形式：
    byte[ ] getBytes( )
    也可使用getBytes( )方法的其他形式。在将字符串（String）值输出到一个不支持16位
    Unicode编码的环境时，getBytes( )是最有用的。

    13.4.4 toCharArray()
    如果想将字符串（String）对象中的字符转换为一个字符数组，最简单的方法就是调用
    toCharArray( )方法。对应整个字符串，它返回一个字符数组。其一般形式为：
    char[ ] toCharArray( )
    这个函数是为了便于使用而提供的，因此也可以用getChars( )方法获得相同的结果。
 */


/*
13.5 字符串比较
    13.5.1 equals()和equalsIgnoreCase()
    使用equals( )方法比较两个字符串是否相等。它具有如下的一般形式：
    boolean equals(Object str)
    这里str是一个用来与调用字符串（String）对象做比较的字符串（String）对象。如果
    两个字符串具有相同的字符和长度，它返回true，否则返回false。这种比较是区分大小写的。
    为了执行忽略大小写的比较，可以调用equalsIgnoreCase( )方法。当比较两个字符串时，
    它会认为A-Z和a-z是一样的。其一般形式如下：
    boolean equalsIgnoreCase(String str)
    这里，str是一个用来与调用字符串（String）对象做比较的字符串（String）对象。如
    果两个字符串具有相同的字符和长度，它也返回true，否则返回false。

    13.5.2 regionMatches()
    regionMatches( )方法将一个字符串中指定的区间和另一字符串中指定的区间进行比
    较。它的重载形式允许在比较时忽略大小写。下面给出这两种方法的一般形式：
    boolean regionMatches(int startIndex, String str2,
    int str2StartIndex, int numChars)
    boolean regionMatches(boolean ignoreCase,
    int startIndex, String str2,
    int str2StartIndex, int numChars)
    对于这两种形式，startIndex指定了调用字符串（String）对象内区间开始的下标。用于
    比较的字符串（String）由str2指定的。在str2内，开始比较区间的下标由str2StartIndex指定。
    用于比较的子字符串的长度在numChars中。在第二种方案中，如果ignoreCase是true，字符
    的大小写被忽略。否则，大小写是有意义的。

    13.5.3 startsWith()和endsWith()
    字符串（String）定义两个例程，它们或多或少是regionMatches( )方法的特殊形式。
    startsWith( )方法判断一个给定的字符串（String）是否从一个指定的字符串开始。相反地，
    endsWith( )方法判断所讨论的字符串（String）是否是以一个指定的字符串结尾。它们具有
    如下的一般形式：
    boolean startsWith(String str)
    boolean endsWith(String str)
    这里，str是被测试的字符串（String）。如果字符串匹配，返回true。否则返回false。
    例如：
    "Foobar".endsWith("bar")
    和
    "Foobar".startsWith("Foo")
    都是true。
    下面给出startsWith( )方法的第二种形式。这种形式允许指定一个起始点：
    boolean startsWith(String str, int startIndex)
    这里，startIndex指定了调用字符串开始搜索的下标。例如，
    "Foobar".startsWith("bar", 3)
    返回true。

    13.5.4 euquals()与==比较
    理解equals( )方法和==运算符执行的是两个不同的操作是重要的。如同刚才解释的那
    样，equals( )方法比较字符串（String）对象中的字符。而==运算符比较两个对象引用看它
    们是否引用相同的实例。

    13.5.5 compareTo
    通常，仅仅知道两个字符串是否相同是不够的。对于排序应用来说，必须知道一个字
    符串是大于、等于还是小于另一个。一个字符串小于另一个指的是它在字典中先出现。而
    一个字符串大于另一个指的是它在字典中后出现。字符串（String）的compareTo( )方法实
    现了这种功能。它的一般形式如下：
    int compareTo(String str)
    这里str是与调用字符串（String）比较的字符串（String）。比较的结果返回并被解释
    如下所示：
    小于0     调用字符串小于str
    大于0     调用字符串大于str
    等于0     两个字符串相等
    如果想在比较两个字符串时，忽略大小写，可以使用如下的compareToIgnoreCase( )方
    法：
    int compareToIgnoreCase(String str)
    除了忽略大小写之外，该方法的返回值与compareTo( )方法相同。
 */


/*
13.6 搜索字符串
String类提供了两个方法，允许在字符串中搜索指定的字符或子字符串：
· indexOf( ) 搜索字符或子字符串首次出现。
· lastIndexOf( ) 搜索字符或子字符串的最后一次出现。
这两种方法被几种不同的方法重载。在所有这些情况下，方法返回字符或子字符串被
发现的位置的下标，当搜索失败时，返回-1。
搜索字符首次出现用
int indexOf(int ch)
搜索字符最后一次出现用
int lastIndexOf(int ch)
这里ch是被查找的字符。
搜索子字符串首次或最后一次出现用
int indexOf(String str)
int lastIndexOf(String str)
这里子字符串由str指定
可以使用如下这些形式指定搜索的起始点：
int indexOf(int ch, int startIndex)
int lastIndexOf(int ch, int startIndex)
int indexOf(String str, int startIndex)
int lastIndexOf(String str, int startIndex)
这里startIndex指定了搜索开始点的下标。对于indexOf( )方法，搜索从startIndex开始到
字符串结束。对于lastIndexOf( )方法，搜索从startIndex开始到下标0。
 */


/*
13.7 修改字符串
因为字符串（String）对象是不可改变的，每当想修改一个字符串（String）时，就必
须采用或者将它复制到StringBuffer或者使用下面字符串（String）方法中的一种，这些方法
都将构造一个完成修改的字符串的拷贝。
    13.7.1 substring()
    利用substring( )方法可以截取子字符串，它有两种形式。其中第一种形式如下：
    String substring(int startIndex)
    这里startIndex指定了子字符串开始的下标。这种形式返回一个从startIndex开始到调用
    字符串结束的子字符串的拷贝。
    substring( )方法的第二种形式允许指定子字符串的开始和结束下标：
    String substring(int startIndex, int endIndex)
    这里startIndex指定开始下标，endIndex指定结束下标。返回的字符串包括从开始下标
    直到结束下标的所有字符，但不包括结束下标对应的字符。

    13.7.2 concat()
    使用concat( )可以连接两个字符串，一般形式如下：
    String concat(String str)
    这个方法创建一个新的对象，该对象包含调用字符串。而str的内容跟在调用字符串的
    后面。concat( )方法与+运算符执行相同的功能。

    13.7.3 replace()
    replace( )方法用另一个字符代替调用字符串中一个字符的所有具体值。它具有如下的
    一般形式：
    String replace(char original, char replacement)
    这里original指定被由replacement指定的字符所代替的字符，返回得到的字符串。

    13.7.4 trim( )
    trim( )方法返回一个调用字符串的拷贝，该字符串是将位于调用字符串前面和后面的空
    白符删除后的剩余部分。它的一般形式如下：
    String trim( )
 */


/*
13.8 利用valueOf()方法实现数据转换
valueOf( )方法将数据的内部格式转换为可读的形式。它是一种静态方法，对于所有Java
内置的类型，在字符串（String）内被重载，以便每一种类型都能被转换成字符串。valueOf( )
方法还被类型Object重载，所以创建的任何形式类的对象也可被用作一个参数（我们知道
Object是所有的类的超类）。这里是它的几种形式：
static String valueOf(double num)
static String valueOf(long num)
static String valueOf(Object ob)
static String valueOf(char chars[ ])
与前面的讨论一样，调用valueOf( )方法可以得到其他类型数据的字符串形式——例如
在进行连接操作时。对各种数据类型，可以直接调用这种方法得到合理的字符串（String）
形式。所有的简单类型数据转换成相应于它们的普通字符串（String）形式。任何传递给
valueOf( )方法的对象都将返回对象的toString( )方法调用的结果。事实上，也可以通过直接
调用toString( )方法而得到相同的结果。
对大多数数组，valueOf( )方法返回一个相当晦涩的字符串，这说明它是一个某种类型
的数组。然而对于字符（char）数组，它创建一个包含了字符（char）数组中的字符的字符
串（String）对象。valueOf( )方法有一种特定形式允许指定字符（char）数组的一个子集。
它具有如下的一般形式：
static String valueOf(char chars[ ], int startIndex, int numChars)
这里chars是存放字符的数组，startIndex是字符数组中期望得到的子字符串的首字符下
标，numChars指定子字符串的长度。
 */


/*
13.9 改变字符串内字符的大小写
toLowerCase( ) 方法将字符串内的所有字符从大写字母改写为小写字母。而
toUpperCase( )方法将字符串内所有字符从小写字母改写为大写字母。对于那些非字母字符，
如数字等则不受影响。下面是这些方法的一般形式：
String toLowerCase( )
String toUpperCase( )
两种方法返回与调用字符串（String）对应的大写或小写的字符串（String）对象。
 */


/*
13.10 StringBuffer
StringBuffer是提供了大量的字符串功能的字符串（String）类的对等类。正如你所知，
字符串（String）表示了定长，不可变的字符序列。相反，StringBuffer表示了可变长的和可
写的字符序列。StringBuffer可有插入其中或追加其后的字符或子字符串。StringBuffer可以
针对这些添加自动地增加空间，同时它通常还有比实际需要更多的预留字符，从而允许增
加空间。
    13.10.1 StringBuffer构造函数
    StringBuffer定义了下面三个构造函数：
    StringBuffer( )
    StringBuffer(int size)
    StringBuffer(String str)
    默认构造函数（无参数）预留了16个字符的空间。该空间不需再分配。第二种形式接
    收一个整数参数，清楚地设置缓冲区的大小。第三种形式接收一个字符串（String）参数，
    设置StringBuffer对象的初始内容，同时不进行再分配地多预留了16个字符的空间。当没有
    指定缓冲区的大小时，StringBuffer分配了16个附加字符的空间，这是因为再分配在时间上
    代价很大。而且频繁地再分配可以产生内存碎片。StringBuffer通过给一些额外的字符分配
    空间，减少了再分配操作发生的次数。

    13.10.2 length( )和capacity( )
    通过调用length( )方法可以得到当前StringBuffer的长度。而通过调用capacity( )方法可
    以得到总的分配容量。它们的一般形式如下：
    int length( )
    int capacity( )

    13.10.3 ensureCapacity( )
    如果想在构造StringBuffer之后为某些字符预分配空间，可以使用ensureCapacity( )方法
    设置缓冲区的大小。这在事先已知要在StringBuffer上追加大量小字符串的情况下是有用的。
    ensureCapacity( )方法具有如下的一般形式：
    void ensureCapacity(int capacity)
    这里capacity指定了缓冲区的大小。

    13.10.4 setLength( )
    使用setLength( )方法在StringBuffer对象内设置缓冲区的大小。其一般形式如下：
    void setLength(int len)
    这里len指定了缓冲区的长度。这个值必须是非负的。
    当增加缓冲区的大小时，空字符将被加在现存缓冲区的后面。如果用一个小于length( )
    方法返回的当前值的值调用setLength( )方法，那么在新长度之后存储的字符将被丢失。

    13.10.5 charAt( )和setCharAt( )
    使用charAt( )方法可以从StringBuffer中得到单个字符的值。可以通过setCharAt( )方法
    给StringBuffer中的字符置值。它们的一般形式如下：
    char charAt(int where)
    void setCharAt(int where, char ch)
    对于charAt( )方法，where指定获得的字符的下标。对于setCharAt( )方法，where指定被
    置值的字符的下标，而ch指定了该字符的新值。对于这两种方法，where必须是非负的，同
    时不能指定在缓冲区之外的位置。

    13.10.6 getChars( )
    使用getChars( )方法将StringBuffer的子字符串复制给数组。其一般形式如下：
    void getChars(int sourceStart, int sourceEnd, char target[ ],
    int targetStart)
    这里，sourceStart指定子字符串开始时的下标，而sourceEnd指定了该子字符串结束时
    下一个字符的下标。这意味着子字符串包含了从sourceStart到sourceEnd–1位置上的字符。
    接收字符的数组由target指定。在target内将被复制子字符串的位置下标由targetStart传递。
    应注意确保target数组足够大以便能够保存指定的子字符串所包含的字符。

    13.10.7 append( )
    append( )方法将任一其他类型数据的字符串形式连接到调用StringBuffer对象的后面。
    对所有内置的类型和Object，它都有重载形式。下面是其几种形式：
    StringBuffer append(String str)
    StringBuffer append(int num)
    StringBuffer append(Object obj)
    每个参数调用String.valueOf( )方法获得其字符串表达式。结果追加在当前StringBuffer
    对象后面。对每一种append( )形式，返回缓冲区本身。它允许后续的调用被连成一串。

    13.10.8 insert( )
    insert( )方法将一个字符串插入另一个字符串中。它被重载而接收所有简单类型的值，
    包括String和Object。和append( )方法一样，它调用String.valueOf( )方法得到调用它的值的
    字符串表达式。随后这个字符串被插入所调用的StringBuffer对象中。下面是它们的几种形
    式：
    StringBuffer insert(int index, String str)
    StringBuffer insert(int index, char ch)
    StringBuffer insert(int index, Object obj)
    这里index指定将字符串插入所调用的StringBuffer对象中的插入点的下标。

    13.10.9 reverse( )
    可以使用reverse( )方法将StringBuffer对象内的字符串翻转，其一般形式如下：
    StringBuffer reverse( )
    这种方法返回被调用对象的翻转对象。

    13.10.10 delete( )和deleteCharAt( )
    这些方法的一般形式如下：
    StringBuffer delete(int startIndex, int endIndex)
    StringBuffer deleteCharAt(int loc)
    delete( )方法从调用对象中删除一串字符。这里startIndex指定了需删除的第一个字符的
    下标，而endIndex指定了需删除的最后一个字符的下一个字符的下标。因此要删除的子字
    符串从startIndex到endIndex–1，返回结果的StringBuffer对象。
    deleteCharAt( )方法删除由loc指定下标处的字符，返回结果的StringBuffer对象。

    13.10.11 replace( )
    它完成在StringBuffer内部用一组字符代替另一组字符的功能。它的形式如下：
    StringBuffer replace(int startIndex, int endIndex, String str)
    被替换的子字符串由下标startIndex和endIndex指定。因此从startIndex到endIndex-1的子
    字符串被替换。替代字符串在str中传递。返回结果的StringBuffer对象。

    13.10.12 substring( )
    它返回StringBuffer的一部分值。它具有如下的两种形式：
    String substring(int startIndex)
    String substring(int startIndex, int endIndex)
    第一种形式返回调用StringBuffer对象中从startIndex下标开始直至结束的一个子字符
    串。第二种形式返回从startIndex开始到endIndex–1结束的子字符串。这些方法与前面在
    String中定义的那些方法具有相同的功能。
 */


class getCharsDemo {
    // 13.4.2
    static void runner() {
        String s = "This is a demo of the getChars method.";
        int start = 10;
        int end = 14;
        char buf[] = new char[end - start];
        s.getChars(start, end, buf, 0);
        System.out.println(buf);
    }
}


class EqualsNotEqualTo {
    // 13.5.4
    static void runner() {
        String s1 = "Hello";
        String s2 = new String(s1);
        System.out.println(s1 + " equals " + s2 + " -> " +
                s1.equals(s2));
        System.out.println(s1 + " == " + s2 + " -> " + (s1 == s2));
        /*
        变量s1指向由“Hello”创建的字符串（String）实例。s2所指的的对象是以s1作为初始
        化而创建的。因此这两个字符串（String）对象的内容是一样的。但它们是不同的对象，这
        就意味着s1和s2没有指向同一的对象，因此它们是不==的。
         */
    }
}


class StringBufferDemo {
    public static void runner() {
        StringBuffer sb = new StringBuffer("Hello");
        System.out.println("buffer = " + sb);
        System.out.println("length = " + sb.length());
        System.out.println("capacity = " + sb.capacity());
        /*
        由于sb在创建时由字符串“Hello”初始化，因此它的长度为5。因为给16个附加的字符
        自动增加了存储空间，因此它的存储容量为21。
         */
    }
}


public class chapter_13 {

    public static void main(String[] args) {
        // 13.4.2
//        getCharsDemo.runner();
        // 13.5.4
//        EqualsNotEqualTo.runner();
        // 13.10.2
        StringBufferDemo.runner();
    }
}
